// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license


// NodeManager.cpp : implementation of the NodeManager class
//

#include "stdafx.h"
#include "WorldBuilder.h"
#include "NodeManager.h"
#include "Node.h"
#include "GeometryNode.h"
#include "MarkerNode.h"
#include "LightManager.h"
#include "MaterialManager.h"
#include "MainView.h"
#include "Mesh.h"
#include "Texture.h"
#include "ShaderProgram.h"
#include "tree_util.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

NodeManager::NodeManager(World* world)
:	m_world(world)
{
}

NodeManager::~NodeManager()
{
    // Check that all listeners have removed themselves
    ASSERT(m_listeners.empty());
}

void NodeManager::addNodeListener(NodeListener * listener)
{
    ASSERT(m_listeners.find(listener) == m_listeners.end());
    m_listeners.insert(listener);
}

void NodeManager::removeNodeListener(NodeListener * listener)
{
    ASSERT(m_listeners.find(listener) != m_listeners.end());
    m_listeners.erase(listener);
}

void NodeManager::clear()
{
    // Ensures that all nodes are kept alive, so that we can notify
    // about their removal after it's taken effect
    tree<ref<Node> > copy(m_nodes);

    // Invalidate all iterators to account for the fact that
    // we are about the clear the tree where they belong
    for(NodeIterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
    {
        (*it)->setIterator(NodeIterator());
    }

    // Clear the tree. Note that the nodes remain alive in "copy",
    // but with invalid iterators
    m_nodes.clear();

    // Notify listeners about each and every node that has been removed
    // TODO this mechanism is terribly inefficient from the point of view
    // of the scene tree view, which rebuilds itself (empty) N times every
    // time a tree of N nodes is cleared. There should be a higher level
    // notification to prevent this ("onNodesClear", or maybe 
    // "onSubTreeRemoved")

    NodeEvent event_(NodeEvent::NodeRemoved);
    event_.manager = this;

    for(ListenerIt it = m_listeners.begin(); 
        it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;

        NodeIterator nodeIt = copy.begin();
        for(; nodeIt != copy.end(); ++nodeIt)
        {
            event_.node = nodeIt->get();
            listener->onNodeEvent(event_);
        }
    }
}

void NodeManager::clearListeners()
{
    m_listeners.clear();
}

void NodeManager::createDummyScene()
{
    // *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
    // Add a root transform node. This is handy because it allows 
    // to move (or trasnform in general) the entire scene at once

    // *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
    // Add a light
    
    ref<LightNode> light(new LightNode);
    light->setName(generateNameFromPrefix(L"light"));
    light->moveLocal(gx::Vec3(1., 0., 0.));
    light->setType(LightTypes::POSITIONAL);
    light->setAmbientColor(getGrey192());
    m_world->getLightManager().registerLight(light);
    
    append(light);

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
    // Add an untextured material
    ref<Material> redMat(new Material);
    redMat->setDiffuseColor(getRed());
    redMat->setName(L"red_matte");
    m_world->getMaterialManager().registerMaterial(redMat);

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
    // Add an untextured transparent material
    ref<Material> blueTransparentMat(new Material);
    blueTransparentMat->setDiffuseColor(getBlue(.5f));
    blueTransparentMat->setName(L"blue_transparent");
    blueTransparentMat->setTransparent();
    m_world->getMaterialManager().registerMaterial(blueTransparentMat);

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
    // Add a few textures 

    TextureManager& tm = m_world->getTextureManager();

    ref<Texture> tex0 = tm.simplePngLoad("texture");
    ref<Texture> tex1 = tm.simplePngLoad("texture2");
    ref<Texture> tex3 = tm.simplePngLoad("transparent");
    ref<Texture> tex4 = tm.simplePngLoad("lines");
    ref<Texture> tex5 = tm.simplePngLoad("frame");
    ref<Texture> tex6 = tm.simplePngLoad("frameblack");
    ref<Texture> tex7 = tm.simpleJpgLoad("crate");
    ref<Texture> tex8 = tm.simpleJpgLoad("l3d");

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
    // Add some textured materials

    ref<Material> texMat(new Material);
    texMat->setName(L"texture");

    texMat->setPrimaryColorMode(PrimaryColorModes::ApplyFirst);
    texMat->addTextureLayer(tex1, EnvironmentModes::Replace);
    texMat->addTextureLayer(tm.getDefaultTexture(), EnvironmentModes::Decal); 
    m_world->getMaterialManager().registerMaterial(texMat);

    ref<Material> texMat2(new Material);
    texMat2->setName(L"texture2");
    texMat2->setPrimaryColorMode(PrimaryColorModes::ApplyLast);
    texMat2->addTextureLayer(tex6, EnvironmentModes::Replace);
    m_world->getMaterialManager().registerMaterial(texMat2);

    ref<Material> texMat3(new Material);
    texMat3->setName(L"texture3");
    texMat3->setPrimaryColorMode(PrimaryColorModes::ApplyFirst);
    //texMat3->addTextureLayer(tex0, EnvironmentModes::Replace);
    texMat3->addTextureLayer(
        tex3, EnvironmentModes::Modulate//,
        //RgbCombinationModes::Replace,
        //AlphaCombinationModes::Replace
    );
    texMat3->setAmbientAndDiffuseColors(Color(.5f, 0.5f, 0.5f, 1.f));
    //texMat3->setTransparent();
    m_world->getMaterialManager().registerMaterial(texMat3);

    ref<Material> crateMat(new Material);
    crateMat->setName(L"crate");
    crateMat->addTextureLayer(tex7, EnvironmentModes::Modulate);
    crateMat->addTextureLayer(tex8, EnvironmentModes::Add);
    m_world->getMaterialManager().registerMaterial(crateMat);


    //lightIt = appendNode(root, Node::create<LightNode>(*this));
    //(*lightIt)->setName(generateNameFromPrefix("light"));
    //(*lightIt)->as<LightNode>()->setType(LightTypes::DIRECTIONAL);
    //m_world->getLightManager().registerLight((*lightIt)->as<LightNode>());

    // *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
    // Add a marker

    //ref<Node> marker(new MarkerNode);
    //marker->setName(generateNameFromPrefix(L"marker"));
    //append(marker);
    
    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Add a few spherical primitive geometries, hierarchically related

    //Node* curr = marker.get();
    //for(int i = 0; i < 10; ++i)
    //{
    //    ref<GeometryNode> node(new GeometryNode);
    //    node->setMaterialId(redMat->getId());
    //    node->setName(generateNameFromPrefix(L"sphere"));
    //    node->setGeometry(ref<Primitive>(new SphereShape));
    //    //sph->scaleLocal(.5);
    //   // (*sphIt)->as<TransformNode>()->rotateLocal(gx::AxisAngle(-gx::K, PI * .25));
    //    node->moveLocal(gx::J * 1.);

    //    append(curr, node);
    //    curr = node.get();
    //}

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Add a cube mesh 
    
    MeshManager& meshMngr = m_world->getMeshManager();

    ref<MeshBuffer> cubeMeshBuff(new MeshBuffer);
    cubeMeshBuff->makeCube();
    cubeMeshBuff->setName(L"cube");
    meshMngr.registerMeshBuffer(cubeMeshBuff);

    if(1)
    {
        ref<GeometryNode> node(new GeometryNode);
        node->setName(L"mesh1");

        ref<Mesh> cube(new Mesh);
        cube->addMeshBuffer(cubeMeshBuff);
        cube->getMeshBufferInstance(0)->setFrontMaterial(crateMat);

        node->setGeometry(cube);
        append(node);
    }

    if(1)
    {
        ref<GeometryNode> node(new GeometryNode);
        node->setName(L"mesh2");

        ref<Mesh> cube(new Mesh);
        ref<MeshBufferInstance> mbi = cube->addMeshBuffer(cubeMeshBuff);
        mbi->setFrontMaterial(crateMat);

        mbi = cube->addMeshBuffer(cubeMeshBuff);
        mbi->setFrontMaterial(crateMat);
        mbi->moveLocal(gx::J);

        node->setGeometry(cube);
        append(node);
    }


#if 0
    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Add a sphere mesh
    if(0)
    {
        ref<GeometryNode> node(new GeometryNode);
        node->setName(L"mesh2");
        ref<Mesh> mesh(new Mesh);
        mesh->makeIndexedSphere();
        node->setGeometry(mesh);
        node->setMaterialId(texMat->getId(), GeometrySides::Front);
        append(node);
    }

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Add an indexed triangle
    if(0)
    {
        ref<GeometryNode> node(new GeometryNode);
        node->setName(L"mesh3");
        ref<Mesh> mesh(new Mesh);
        mesh->makeIndexedTriangle();
        node->setGeometry(mesh);
        node->setMaterialId(texMat3->getId(), GeometrySides::Front);
        append(node);
    }

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Add an indexed cylinder
    if(0)
    {
        ref<GeometryNode> node(new GeometryNode);
        node->setName(L"mesh4");
        ref<Mesh> mesh(new Mesh);
        mesh->makeIndexedCylinder(.5f, 1.f, 20);
        node->setGeometry(mesh);
        node->setMaterialId(texMat->getId(), GeometrySides::Front);
        node->setMaterialId(texMat2->getId(), GeometrySides::Back);
        append(node);
    }
#endif
    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Add some shader programs

    ShaderManager& sm = m_world->getShaderManager();
    
    sm.simpleLoad("minimal");
    sm.simpleLoad("color");
    sm.simpleLoad("flatten", ShaderManager::IsTimedShader)
        ->setUniformVar("time", 1.f);
    sm.simpleLoad("basic_tex");
    sm.simpleLoad("tex_and_frag");
    sm.simpleLoad("2tex_mult");
    sm.simpleLoad("2tex_add");
    sm.simpleLoad("2tex_glow");
    sm.simpleLoad("toon");
    sm.simpleLoad("toon2");
    sm.simpleLoad("toon3");
    sm.simpleLoad("light");
    sm.simpleLoad("perPixelLight");
    sm.simpleLoad("spotLight");
}

//void NodeManager::eraseBlock(const Node & node)
//{
//	ASSERT(m_nodes.find(node) != m_nodes.end());
//	m_nodes.erase(node);
//	
//	notifyListeners();
//}
//
//void NodeManager::replaceShape(const Node & node, ref<Shape> shape)
//{
//	BlockMap::iterator it = m_nodes.find(node);
//	ASSERT(it != m_nodes.end());
//	it->second = shape;
//
//	notifyListeners();
//}

void NodeManager::appendTree(const NodeTree& other)
{
    // This produces sibling iterators!
    appendSiblings(other.begin(), other.end());
}

void NodeManager::appendSiblings(NodeSiblingIterator begin, NodeSiblingIterator end)
{
    for(NodeSiblingIterator it = begin; it != end; ++it)
    {
        appendBranch(it);
    }
}

void NodeManager::appendBranch(NodeIterator root)
{
    m_nodes.insert_subtree(m_nodes.begin(), root);

    // Update each node's iterator
    for(NodeIterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
    {
        (*it)->setIterator(it);
    }

    NodeEvent event_(NodeEvent::NodeAdded);
    event_.manager = this;

    for(ListenerIt it = m_listeners.begin(); it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;
        
        event_.node = root->get();
        listener->onNodeEvent(event_);
        
        for(NodeIterator nodeIt = root.begin(); nodeIt != root.end(); ++nodeIt)
        {
            event_.node = nodeIt->get();
            listener->onNodeEvent(event_);
        }
    }
}

void NodeManager::append(ref<Node> node)
{
    node->setNodeManager(this);

    NodeIterator it;
    if(m_nodes.empty())
    {
        it = m_nodes.set_head(node);
    }
    else
    {
        it = m_nodes.insert(m_nodes.begin(), node);
    }

    node->setIterator(it);

    NodeEvent event_(NodeEvent::NodeAdded);
    event_.manager = this;
    for(ListenerIt it = m_listeners.begin(); it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;
        event_.node = node.get();
        listener->onNodeEvent(event_);
    }
}

void NodeManager::append(Node* parent, ref<Node> node)
{
    node->setNodeManager(this);

    NodeIterator it = m_nodes.append_child(parent->getIterator(), node);
    node->setIterator(it);

    NodeEvent event_(NodeEvent::NodeAdded);
    event_.manager = this;
    for(ListenerIt it = m_listeners.begin(); it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;
        event_.node = node.get();
        listener->onNodeEvent(event_);
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void NodeManager::remove(Node* node)
{
    ref<Node> copy = node->getRef<Node>();

    Node* parent = node->getParent();

    // If the node is not top-level, simply make its children 
    // children of the node's parent (i.e., bypass the node)
    if(parent != NULL)
    {
        m_nodes.reparent(node->getParent()->getIterator(), 
                         node->getIterator().begin(),
                         node->getIterator().end());
        node->getParent()->invalidateGlobalTransform();
    }
    // If the node is top-level, make its children top level
    else
    {
        for(NodeSiblingIterator it = node->getIterator().begin();
            it != node->getIterator().end();)
        {
            NodeSiblingIterator next = it;
            ++next;
            m_nodes.move_after(m_nodes.begin(), NodeIterator(it));
            it = next;
        }
    }

    // Finally remove the node itself
    m_nodes.erase(node->getIterator());

    //And invalidate its iterator
    node->setIterator(NodeIterator());
    
    // And notify the listeners
    // TODO we should notify about the reparented nodes
    NodeEvent event_(NodeEvent::NodeRemoved);
    event_.manager = this;
    for(ListenerIt it = m_listeners.begin(); 
        it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;
        
        event_.node = copy.get();
        listener->onNodeEvent(event_);
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void NodeManager::removeBranch(Node* node)
{
    tree<ref<Node> > copy(node->getIterator());

    m_nodes.erase_children(node->getIterator());
    m_nodes.erase(node->getIterator());

    // TODO this is duplicated in "clear"
    NodeEvent event_(NodeEvent::NodeRemoved);
    event_.manager = this;
    for(ListenerIt it = m_listeners.begin(); 
        it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;

        NodeIterator nodeIt = copy.begin();
        for(; nodeIt != copy.end(); ++nodeIt)
        {
            event_.node = nodeIt->get();
            listener->onNodeEvent(event_);
        }
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void NodeManager::removeChildren(Node* node)
{
    NodeIterator it = node->getIterator();
    tree<ref<Node> > copy(NodeIterator(it.begin()));

    m_nodes.erase_children(it);

    // TODO this is duplicated in "clear" and in "removeBranch"
    NodeEvent event_(NodeEvent::NodeRemoved);
    event_.manager = this;
    for(ListenerIt it = m_listeners.begin();
        it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;

        NodeIterator nodeIt = copy.begin();
        for(; nodeIt != copy.end(); ++nodeIt)
        {
            event_.node = nodeIt->get();
            listener->onNodeEvent(event_);
        }
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


bool NodeManager::hasName(const std::wstring & name) const
{
    for(NodeIterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
    {
        if((*it)->getName() == name)
        {
            return true;
        }
    }
    return false;
}

std::wstring NodeManager::generateNameFromPrefix(const std::wstring & prefix) const
{
    for(int i = 0; ; ++i) // Will eventually find an unused name
    {
        std::wostringstream oss;
        oss << prefix << i;
        if(!hasName(oss.str()))
        {
            return oss.str();
        }
    }
}

Node* NodeManager::getNodeByName(const std::wstring& name) const
{
    for(NodeIterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
    {
        if((*it)->getName() == name)
        {
            return it->get();
        }
    }
    return NULL;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


void NodeManager::reparent(Node* node, Node* newParent)
{
    //Node* oldParent = node->getParent();

    // This is necessary because tree<>::reparent(pos, from) does not move
    // the node "from" itself, but all of its children. Thus we must use anoher
    // version of reparent, which takes a range of siblings to relocate. But
    // we only want to move one ("node"), so we pass the blockIt, blockIt + 1 range
    NodeSiblingIterator sib = NodeSiblingIterator(node->getIterator()); 
    
    NodeSiblingIterator nextToSib = sib; 
    ++nextToSib;
    m_nodes.reparent(newParent->getIterator(), 
                     sib, nextToSib);
    node->invalidateGlobalTransform();
    
    NodeEvent event_(NodeEvent::NodeParentChanged);
    event_.node = node;
    event_.manager = this;
    ListenerIt it = m_listeners.begin();
    for(; it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;
        listener->onNodeEvent(event_);
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
void NodeManager::notifyNodePropertyChanged(Node* node, 
                                            NodeEvent::Property property_)
{
    NodeEvent event_(NodeEvent::NodePropertyChanged, property_);
    event_.manager = this;
    event_.node = node;
    
    ListenerIt it = m_listeners.begin();
    for(; it != m_listeners.end(); ++it)
    {
        NodeListener* listener = *it;
        listener->onNodeEvent(event_);
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

namespace
{

enum { LIGHT_NODE, SHAPE_NODE, TRANSFORM_NODE, MARKER_NODE };

int nodeToType(const Node* node)
{
    if(dynamic_cast<const LightNode*>(node))
    {
        return LIGHT_NODE;
    }
    if(dynamic_cast<const GeometryNode*>(node))
    {
        return SHAPE_NODE;
    }
    if(dynamic_cast<const MarkerNode*>(node))
    {
        return MARKER_NODE;
    }
    if(dynamic_cast<const TransformNode*>(node))
    {
        return TRANSFORM_NODE;
    }
    ASSERT(false);
    return -1;
}

ref<Node> typeToNode(int type)
{
    ref<Node> result;
    switch(type)
    {
    case LIGHT_NODE:
        result.reset(new LightNode);
        break;
    case SHAPE_NODE:
        result.reset(new GeometryNode);
        break;
    case TRANSFORM_NODE:
        result.reset(new TransformNode);
        break;
    case MARKER_NODE:
        result.reset(new MarkerNode);
        break;
    default:
        ASSERT(false);
    }
    return result;
}

} // Anonymous namespace


void NodeManager::writeNode(std::wostream& os, ref<const Node> node) const
{
    int type = nodeToType(node.get());
    os << type << "\n";
    node->write(os);
    os << "\n";
    
    Node* parent = node->getParent();
    if(parent == NULL)
    {
        os << "__root__";
    }
    else
    {
        os << parent->getName();
    }
    os << "\n";
}

void NodeManager::readNode(std::wistream& is)
{
    // Find out what type this node is
    
    int type;
    is >> type;
    ref<Node> node = typeToNode(type);
    
    node->read(is);

    std::wstring parentName;
    is >> parentName;

    // TODO make the light manager be a node listener and take
    // care of this on his own
    if(type == LIGHT_NODE)
    {
        LightManager& lm = m_world->getLightManager();
        lm.registerLight(dyn_ptr_cast<LightNode>(node));
    }

    // Set node's parent (this is one of the reasons why node reading
    // has to be done at the node manager level)
    if(parentName == L"__root__")
    {
        append(node);
    }
    else
    {
        Node* parent = getNodeByName(parentName);
        append(parent, node);
    }
}
